home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Snippets / Stuart's Tech Notes / SmartZoomWindow.c < prev    next >
Text File  |  1995-06-08  |  7KB  |  159 lines

  1. /* (C) June 1994 Stuart Cheshire <cheshire@cs.stanford.edu>
  2.  
  3. In a stunning piece of developer support, when Apple added the zoom box
  4. to the Window Manager, the user interface police  mandated how it should
  5. work in a user-friendly way, and then provided a system call that didn't
  6. do it!  Considering that at the time many developers (especially shareware
  7. authors) didn'it have access to large screens or multiple-monitor setups,
  8. expecting every developer to engineer their own user-friendly multiple-
  9. monitor-aware solution was ridiculous.
  10.  
  11. Having just been through this grief for Bolo, I'm making the solution
  12. available for other developers.
  13.  
  14. SmartZoomWindow is written to be a simple drop-in replacement for Apple's
  15. ZoomWindow call so you shouldn't have to make much change to your program.
  16. The first three parameters are the same, and the fourth is a pointer to a
  17. Rectangle giving the maximum and minimum sensible dimensions that the window
  18. should be zoomed to.
  19.  
  20.     ZoomWindow(WindowPtr theWindow, short partCode, Boolean front);
  21.     SmartZoomWindow(WindowPtr win, short partCode, Boolean front, const Rect *Limits);
  22.  
  23. The Rect is defined in a way compatible to GrowWindow -- ie top,left give the
  24. minimum height and width of the window, and bottom, right give the maximum
  25. height and width of the window *plus one*. Note that if you want the maximum
  26. window size to be 100x100, this means that you have to set the limits to 101.
  27. I know that this is bizarre, but it's the way that GrowWindow works, even
  28. though that's not what Inside Macintosh says. (As an aside, I wonder how
  29. many programs are bitten by this? I know the Finder has this bug -- open the
  30. "About This Macintosh" window and try resizing it with the zoom box and with
  31. the grow box. If you look closely you will see that when you repeatedly click
  32. on the zoom box, the width oscillates in and out by one pixel.)
  33.  
  34. If you don't have any special constraints on what size the window should
  35. be, you can set the minimums to zero and the maximums to 0x7FFF, and
  36. SmartZoomWindow will just take care of zooming the window up to the full
  37. size of the monitor that it is on, instead of zooming it back to the main
  38. (menu bar) screen like the normal ZoomWindow call does.
  39.  
  40. After calling WaitNextEvent and FindWindow as usual, your code should proceed
  41. like this:
  42.  
  43.     if (TrackBox(win, theEventRecord->where, partcode))
  44.         {
  45.         Rect Limits = { 0, 0, 0x7FFF, 0x7FFF };  // Or whatever limits are appropriate
  46.         SetPort(win);
  47.         EraseRect(&win->portRect);
  48.         SmartZoomWindow(win, partcode, TRUE, &Limits);
  49.         }
  50.  
  51. SmartZoomWindow is slightly smarter than the algorithm that the Finder uses,
  52. which makes the windows fly up to the top left corner of the monitor if
  53. resizing them in the current position would result in part of the window going
  54. off the screen. SmartZoomWindow will just move the window the minimum amount
  55. required to keep it on the screen.
  56.  
  57. Some of the inspiration for this code came from DoBetterWZoom.c on the Apple
  58. Developer CD. I would credit the author, but there is no name on that code.
  59. */
  60.  
  61. #include <StuTypes.h>
  62.  
  63. local GDHandle findDominantDevice(Rect *theRect)
  64.     {
  65.     long sectArea, greatestArea = 0;
  66.     GDHandle dominantGDevice = NULL, dev = GetDeviceList();
  67.     while (dev)                                                // for every device
  68.         {
  69.         if (TestDeviceAttribute(dev, screenDevice) &&        // that's an active screen
  70.             TestDeviceAttribute(dev, screenActive))
  71.             {
  72.             Rect r;
  73.             SectRect(theRect, &(**dev).gdRect, &r);        // find out the intersection area
  74.             sectArea = (long)(r.right - r.left) * (long)(r.bottom - r.top);
  75.             if (greatestArea < sectArea) { greatestArea = sectArea; dominantGDevice = dev; }
  76.             }
  77.         dev = GetNextDevice(dev);
  78.         }
  79.     return(dominantGDevice);
  80.     }
  81.  
  82. // The window will be zoomed to fill the screen it is already on,
  83. // subject to the size constraints of the Limits rectangle.
  84. // win           = window to be zoomed
  85. // partCode      = inZoomIn or inZoomOut
  86. // front         = Bring window to front after zooming? (Normally TRUE)
  87. // Limits.top    = minimum allowable window height
  88. // Limits.left   = minimum allowable window width
  89. // Limits.bottom = maximum sensible window height + 1
  90. // Limits.right  = maximum sensible window width  + 1
  91. // Note: The maximums are actually one greater than the actual maximum allowed sizes,
  92. // for consistency with GrowWindow
  93. local void SmartZoomWindow(WindowPtr win, short partCode, Boolean front, const Rect *Limits)
  94.     {
  95.     // Zooming in is easy. Only zooming out needs extra smarts
  96.     if (partCode == inZoomOut)
  97.         {
  98.         SysEnvRec sysenvirons;
  99.         short      hsize            = Limits->right  - 1;
  100.         short      vsize            = Limits->bottom - 1;
  101.         WindowPeek wp               = (WindowPeek)win;
  102.         Rect       *zoomRect        = &(*(WStateDataHandle)wp->dataHandle)->stdState;
  103.         Rect       *globalFrameRect = &(*wp->strucRgn)->rgnBBox;
  104.         Rect       globalPortRect   = win->portRect;
  105.         Rect       target           = qd.screenBits.bounds;    // Start with a reasonable default
  106.         target.top += GetMBarHeight();
  107.         LocalToGlobal(&topLeft(globalPortRect));        // calculate the window's portRect
  108.         LocalToGlobal(&botRight(globalPortRect));        // in global coordinates
  109.         
  110.         // *** TASK 1
  111.         // Pick a monitor (if necessary) and work out the maximum allowable rectangle on it
  112.         SysEnvirons(1,&sysenvirons);
  113.         if (sysenvirons.hasColorQD)
  114.             {
  115.             GDHandle dev = findDominantDevice(globalFrameRect);
  116.             if (dev)
  117.                 {
  118.                 target = (*dev)->gdRect;
  119.                 if (dev == GetMainDevice()) target.top += GetMBarHeight();
  120.                 }
  121.             }
  122.  
  123.         // *** TASK 2
  124.         // Reduce the logical target rectangle a little to allow for the frame
  125.         // around the window, and a couple of pixels extra for aesthetic reasons
  126.         target.top    += (globalPortRect.top    - globalFrameRect->top   ) + 2;
  127.         target.left   += (globalPortRect.left   - globalFrameRect->left  ) + 2;
  128.         target.bottom += (globalPortRect.bottom - globalFrameRect->bottom) - 1;
  129.         target.right  += (globalPortRect.right  - globalFrameRect->right ) - 1;
  130.  
  131.         // *** TASK 3
  132.         // Make sure the target rectangle is big enough for the minimum size
  133.         if (target.bottom < target.top  + Limits->top ) target.bottom = target.top  + Limits->top;
  134.         if (target.right  < target.left + Limits->left) target.right  = target.left + Limits->left;
  135.  
  136.         // *** TASK 4
  137.         // Make sure the desired (maximum) size fits within the target rectangle
  138.         if (vsize > target.bottom - target.top) vsize = target.bottom - target.top;
  139.         if (hsize > target.right - target.left) hsize = target.right - target.left;
  140.         
  141.         // *** TASK 5
  142.         // Start off with a zoom rectangle obtained by simply resizing the window
  143.         // in the current position
  144.         zoomRect->top    = globalPortRect.top;
  145.         zoomRect->left   = globalPortRect.left;
  146.         zoomRect->bottom = globalPortRect.top  + vsize;
  147.         zoomRect->right  = globalPortRect.left + hsize;
  148.  
  149.         // *** TASK 6
  150.         // If Window is falls outside the limits, shift it the minimum amount to bring it inside
  151.         if (zoomRect->top    < target.top   ) OffsetRect(zoomRect, 0, target.top - zoomRect->top);
  152.         if (zoomRect->left   < target.left  ) OffsetRect(zoomRect, target.left - zoomRect->left, 0);
  153.         if (zoomRect->bottom > target.bottom) OffsetRect(zoomRect, 0, target.bottom - zoomRect->bottom);
  154.         if (zoomRect->right  > target.right ) OffsetRect(zoomRect, target.right - zoomRect->right, 0);
  155.         }
  156.     ZoomWindow(win, partCode, front);
  157.     }
  158.  
  159.